﻿using Sustainalytics.Entities;
using Sustainalytics.OAuth.Services.Common;
using Sustainalytics.Portfolios.BL;
using Sustainalytics.Portfolios.BL.Model;
using Sustainalytics.ScreeningTool.BL;
using Sustainalytics.ScreeningTool.BL.Exceptions;
using Sustainalytics.ScreeningTool.Service.Models;
using Sustainalytics.ScreeningTool.Service.Utility;
using Sustainalytics.Universes.BusinessLogic;
using Sustainalytics.Utils;
using Sustainalytics.Utils.Serilog;
using Sustainalytics.Utils.WebApi.ErrorCodes;
using System;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http;
using System.Web.Http.Cors;

namespace Sustainalytics.ScreeningTool.Service.Controllers
{
    [EnableCors(origins: "*", headers: "*", methods: "*")]
    [RoutePrefix("api/v1/portfolios")]
    public class PortfolioController : ApiController
    {
        private readonly ICompanyScreening _companyScreening;
        private readonly IPortfolioReadHandler _portfolioReadHandler;
        private readonly ICustomPortfolioUpdateHandler _portfolioUpdateHandler;
        private readonly IUniversesManagement _universesManagement;

        public PortfolioController(
            PortfolioReadHandler portfolioReadHandler,
            CustomPortfolioUpdateHandler portfolioUpdateHandler,
            ICompanyScreening companyScreening,
            IUniversesManagement universesManagement)
        {
            try
            {
                CommonEventSerilog.Log.InitController();
                
                _portfolioReadHandler = portfolioReadHandler;
                _portfolioUpdateHandler = portfolioUpdateHandler;
                _universesManagement = universesManagement;
                _companyScreening = companyScreening;

                CommonEventSerilog.Log.InitControllerSuccess();
            }
            catch (Exception ex)
            {
                CommonEventSerilog.Log.InitControllerError(ex);
                throw new HttpResponseException(HttpStatusCode.InternalServerError);
            }           
        }

        [Route("universesandportfolios")]
        public HttpResponseMessage Get(
            [FromUri] ListOptions listOptions,
            Guid accountId,
            Guid userId,
            string namePortfolio,
            bool? isMyPortfolio,
            bool isNameEqual = false)
        {
            try
            {
                var userClaims = new PortfolioUserClaimsProcessor().GetPortfolioClaims();

                PortfolioFilter filter = new PortfolioFilter
                {

                    Name = namePortfolio,
                    SharingStatus = PortfolioSharingStatus.All,
                    AddedByUserId = userClaims.UserId,
                    IsNameEqual = isNameEqual,
                    AccountId = userClaims.AccountId,
                    IsMyPortfolio = isMyPortfolio,
                    PortfolioType = PortfolioTypes.All
                };

                var porfolios = this._portfolioReadHandler.GetPortfolios(
                    filter,
                    /*listOptions.Skip*/null,
                    /*listOptions.Take*/null,
                    listOptions.OrderBy,
                    listOptions.OrderAscending);

                var portfolioList = porfolios.Select(x => new PortfolioUniversesDto
                {
                    Name = x.Name,
                    Id = x.Id.ToString(),
                    Type = this.GetResultType(x, userClaims)
                });

                var universesEntities =
                    this._universesManagement.GetUniversesByNameForAccountId(accountId, namePortfolio)
                        .Select(
                            x => new PortfolioUniversesDto { Name = x.Name, Id = x.Id.ToString(), Type = PortfolioUniversesType.Universe })
                        .ToList();

                var universesDtoList =
                    universesEntities.Union(portfolioList).OrderBy(u => u.Name).ToList();



                if (listOptions.Skip != 0 && listOptions.Take != 0)
                {
                    return this.Request.CreateResponse(HttpStatusCode.OK, universesDtoList.Skip(listOptions.Skip).Take(listOptions.Take));
                }

                return this.Request.CreateResponse(HttpStatusCode.OK, universesDtoList);
            }
            catch (AuthorizationException)
            {
                var customMessage = "Not authorized";
                if (userId == default(Guid))
                {
                    customMessage = "Not authorized. Please attempt re-login.";
                }

                return this.Request.CreateResponse(
                    HttpStatusCode.Unauthorized,
                    new
                    {
                        ErrorStatusCode = WebAPIGeneralErrorCodes.AuthorizationFailed,
                        message = customMessage,
                        Details = listOptions
                    });
            }
            catch (Exception ex)
            {
                throw new HttpResponseException(HttpStatusCode.InternalServerError);
            }
        }

        private PortfolioUniversesType GetResultType(Portfolio x, PortfolioUserClaims user)
        {
            if (x.IsAutomatic)
            {
                return PortfolioUniversesType.SystemPortfolio;
            }

            return x.IsMyCustomPortfolio(user.UserName) ? PortfolioUniversesType.MyPortfolios : PortfolioUniversesType.SharedPortfolios;
        }

        [Route("")]
        public HttpResponseMessage Post([FromBody] PortfolioAddDto portfolioAdd)
        {
            if (portfolioAdd.IsValid == null)
            {
                return new HttpResponseMessage(HttpStatusCode.BadRequest);
            }

            if (string.IsNullOrEmpty(portfolioAdd.PortfolioName))
            {
                return new HttpResponseMessage(HttpStatusCode.BadRequest);
            }

            try
            {
                var filter = BuildResultOutputFilter(portfolioAdd);

                var result = this._companyScreening.GetFullResultOutput(filter);

                var userClaims = new PortfolioUserClaimsProcessor().GetPortfolioClaims();

                var companyIds = result.Select(data => data["CompanyId"].AsGuid).ToList();

                var insertSecuritiesResult =
                    this._portfolioUpdateHandler.AddCompaniesToPortfolio(
                        portfolioAdd.PortfolioId,
                        portfolioAdd.PortfolioName,
                        portfolioAdd.SharingStatus,
                        companyIds,
                        userClaims);

                if (insertSecuritiesResult.SavedWithErrors)
                {
                    return this.Request.CreateResponse(HttpStatusCode.OK, new { WarningCode = 5105 });
                }

                return this.Request.CreateResponse(HttpStatusCode.OK);
            }
            catch (Exception ex)
            {

                return this.Request.CreateResponse(HttpStatusCode.InternalServerError);
            }
        }

        [Route("")]
        public HttpResponseMessage Put([FromBody] PortfolioAddDto portfolioAdd)
        {
            if (portfolioAdd.IsValid == null)
            {
                return new HttpResponseMessage(HttpStatusCode.BadRequest);
            }

            if (string.IsNullOrEmpty(portfolioAdd.PortfolioName))
            {
                return new HttpResponseMessage(HttpStatusCode.BadRequest);
            }

            try
            {
                var filter = BuildResultOutputFilter(portfolioAdd);

                var result = this._companyScreening.GetFullResultOutput(filter);

                var userClaims = new PortfolioUserClaimsProcessor().GetPortfolioClaims();

                var companyIds = result.Select(data => data["CompanyId"].AsGuid).ToList();

                var insertWithNotAllSecurities =
                    this._portfolioUpdateHandler.AddCompaniesToPortfolio(
                        portfolioAdd.PortfolioId,
                        portfolioAdd.PortfolioName,
                        portfolioAdd.SharingStatus,
                        companyIds,
                        userClaims);

                if (insertWithNotAllSecurities.SavedWithErrors)
                {
                    return this.Request.CreateResponse(HttpStatusCode.OK, new { WarningCode = 5105 });
                }

                return this.Request.CreateResponse(HttpStatusCode.OK);
            }
            catch (Exception ex)
            {
                return this.Request.CreateResponse(HttpStatusCode.InternalServerError);
            }
        }

        private static ResultOutputFilter BuildResultOutputFilter(PortfolioAddDto portfolioAdd)
        {
            var filter = new ResultOutputFilter { FilterId = portfolioAdd.FilterId, IsValid = portfolioAdd.IsValid };
            return filter;
        }
    }

    public enum PortfolioUniversesType
    {
        MyPortfolios = 0,

        SharedPortfolios,

        Universe,

        SystemPortfolio
    }

    public class PortfolioUniversesDto
    {
        public string Id { get; set; }

        public string Name { get; set; }

        public PortfolioUniversesType Type { get; set; }
    }
}